package com.iplatform.file.minio.api.v1;

import java.io.*;
import java.lang.reflect.Field;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipInputStream;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ZipUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.PageInfo;
import com.iplatform.commons.exception.CustomException;
import com.iplatform.commons.utils.CommResponse;
import com.iplatform.file.minio.core.*;
import io.minio.PutObjectOptions;
import io.swagger.annotations.*;
import org.apache.commons.io.FileUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ibatis.annotations.Param;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import com.iplatform.commons.utils.SpringStyleResponseUtil;

import io.minio.errors.ErrorResponseException;
import io.minio.errors.InvalidBucketNameException;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author red
 */

@RestController
@RequestMapping(value = "api/v1/minio")
@Api(tags = "Minio文件服务接口")
public class ApiMinioController {


    Log log = LogFactory.getLog(ApiMinioController.class);


    @Autowired
    private MinioClientHelper minioClientHelper;


    @Autowired
    private MinioConfig minioConfig;


    /**
     * 默认桶
     */
    @Value("${minio.default-bucket}")
    private String defaultBucket;


    @Value("${chart.file.url}")
    private String chartFileUrl;

    @Value("${chart.file.projectUrl}")
    private String projectUrl;


    @Value("${chart.file.minio-bucke}")
    private String chartFileMinioBucke;


    @Value("${chart.file.cacheFileDir}")
    private String cacheFileDir;


    /**
     * minio服务地址
     */
    @Value("${minio.minio-url}")
    private String minioUrl;

    @PostMapping(value = "presignedPutUrl")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "objPath", required = true, value = "文件路径"),
            @ApiImplicitParam(name = "bucketName", value = "桶名，如果桶名为空使用默认桶，如果桶不存在则创建桶"),
            @ApiImplicitParam(name = "expires", value = "过期时间，以秒为单位")
    })
    @ApiResponses({
            @ApiResponse(code = 200, message = "获取成功", response = MinioObject.class)
    })
    @ApiOperation(value = "获取上传预签名地址")
    public MinioObject postPresignedPutUrl(@RequestParam(name = "objPath") String objPath,
                                           @RequestParam(name = "bucketName") String bucketName,
                                           @RequestParam(name = "expires", required = false) Integer expires) throws Exception {
        if(expires == null){
            expires = 0;
        }
        return new MinioObject(minioClientHelper.presignedPutObject(objPath, bucketName, expires), objPath);
    }

//    @PostMapping(value = "presignedPutUrl2")
//    @ApiImplicitParams({
//            @ApiImplicitParam(name = "objName", required = true, value = "文件名")
//    })
//    @ApiResponses({
//            @ApiResponse(code = 200, message = "获取成功", response = MinioObjectDto.class)
//    })
//    @ApiOperation(value = "获取上传预签名地址")
//    public MinioObjectDto postPresignedPutUrl(@RequestParam(value = "objName") String objName) throws Exception {
//        Calendar calendar = Calendar.getInstance();
//        Integer year = calendar.get(Calendar.YEAR);
//        Integer month = calendar.get(Calendar.MONTH) + 1;
//        String objPath = year + "/" + month + "/" + calendar.getTimeInMillis() + "." + objName;
//        return new MinioObjectDto(objPath, minioClientHelper.presignedPutObject(objPath,null,0), null);
//    }


    @PostMapping(value = "presignedUrl")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "fileName", required = true, value = "文件名"),
            @ApiImplicitParam(name = "bucketName", value = "桶名，如果桶名为空使用默认桶，如果桶不存在则创建桶"),
            @ApiImplicitParam(name = "expires", value = "过期时间，以秒为单位"),
            @ApiImplicitParam(name = "type", value = "1:图表,2:图册")
    })
    @ApiResponses({
            @ApiResponse(code = 200, message = "获取成功", response = MinioObject.class)
    })
    @ApiOperation(value = "获取上传预签名地址(图表、图册)")
    public MinioObject postPresignedPutUrl(@RequestParam(name = "fileName") String fileName,
                                           @RequestParam(name = "bucketName") String bucketName,
                                           @RequestParam(name = "expires", required = false) Integer expires,
                                           @RequestParam(name = "type", required = true) Integer type) throws Exception {

        log.info("上传文件名：" + fileName);
        //获取文件后缀
        String substring = fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length());

        UUID uuid = UUID.randomUUID();
        String replace = uuid.toString().replace("-", "");
        String objPath = null;
        if (type != null) {
            //保存图表
            if (type == 1) {

                objPath = "chart" + "/" + replace + "." + substring;
                log.info("图表上传：" + objPath);
                return new MinioObject(minioClientHelper.presignedPutObject(objPath, bucketName, expires), objPath);
            }
            //保存图册
            if (type == 2) {
                objPath = "chartbook" + "/" + replace + "." + substring;
                return new MinioObject(minioClientHelper.presignedPutObject(objPath, bucketName, expires), objPath);
            }
        }
        return null;
    }

//    @PostMapping(value = "presignedGetUrl2")
//    @ApiOperation(value = "获取文件下载地址")
//    @ApiImplicitParams({
//            @ApiImplicitParam(name = "objPath", required = true, value = "文件相对路径"),
//            @ApiImplicitParam(name = "expires", required = false, value = "过期时间，以秒为单位")
//    })
//    @ApiResponses({
//            @ApiResponse(code = 200, message = "获取成功", response = MinioObjectDto.class)
//    })
//    public MinioObjectDto postPresignedGetUrl(@RequestParam(value = "objPath") String objPath, @RequestParam(value = "expires", required = false) Integer expires) throws Exception {
//        return new MinioObjectDto(objPath, null, minioClientHelper.presignedGetObject(objPath,null, expires));
//    }


    @PostMapping(value = "presignedGetUrl")
    @ApiOperation(value = "获取文件下载地址")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "objPath", required = true, value = "文件相对路径"),
            @ApiImplicitParam(name = "bucketName", value = "桶名"),
            @ApiImplicitParam(name = "expires", value = "过期时间，以秒为单位")
    })
    @ApiResponses({
            @ApiResponse(code = 200, message = "获取成功", response = MinioObject.class)
    })
    public MinioObject postPresignedGetUrl(@RequestParam(name = "objPath") String objPath,
                                           @RequestParam(name = "bucketName") String bucketName,
                                           @RequestParam(name = "expires", required = false) Integer expires) throws Exception {
        MinioObject ss = new MinioObject(minioClientHelper.presignedGetObject(objPath, bucketName, expires), objPath);
        System.out.println(ss.getUrl());
        return ss;
    }


    @PostMapping(value = "/uploadFile", produces = {MediaType.APPLICATION_JSON_UTF8_VALUE}, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    @ApiOperation(value = "上传文件")
    public void uploadFile(@RequestParam(name = "objPath", required = true) String objPath,
                           @RequestParam(name = "bucketName", required = false) String bucketName,
                           @RequestPart(value = "file") MultipartFile file) throws Exception {
        if (file.isEmpty()) {
            return;
        }

        minioClientHelper.getMinioClient().putObject(StringUtils.isEmpty(bucketName) ? defaultBucket : bucketName, objPath, file.getInputStream(), new PutObjectOptions(file.getInputStream().available(), -1));
    }


    @PostMapping(value = "/replaceFileContent")
    @ApiOperation(value = "上传替换文件内容")
    public String replaceFileContent(@RequestParam(name = "sourcePath", required = true) String sourcePath,
                                     @RequestParam(name = "objPath", required = true) String objPath,
                                     @RequestParam(name = "bucketName", required = false) String bucketName,
                                     @RequestParam(name = "replaceKey", required = true) String replaceKey,
                                     @RequestParam(name = "replaceString", required = true) String replaceString) throws Exception {


        InputStream inputStream = minioClientHelper.getMinioClient().getObject(StringUtils.isEmpty(bucketName) ? defaultBucket : bucketName, sourcePath);
        if (inputStream != null) {
            String fileContent = toConvertStringAndReplace(inputStream, replaceString, replaceKey);
            if (!StringUtils.isEmpty(fileContent)) {
                InputStream in_nocode = new ByteArrayInputStream(fileContent.getBytes());
                try {
                    PutObjectOptions putObjectOptions = new PutObjectOptions(in_nocode.available(), -1);
                    putObjectOptions.setContentType("application/x-javascript");
                    minioClientHelper.getMinioClient().putObject(StringUtils.isEmpty(bucketName) ? defaultBucket : bucketName, objPath, in_nocode, putObjectOptions);

                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    if (in_nocode != null) {
                        in_nocode.close();
                    }
                }
                return minioConfig.getMinioUrl() + "/" + (StringUtils.isEmpty(bucketName) ? defaultBucket : bucketName) + "/" + objPath + "?X-Amz-Algorithm=AWS4-HMAC-SHA256";
            }

        }
        return "";
    }

    @PostMapping(value = "/uploadOrReplaceChartFile", produces = {MediaType.APPLICATION_JSON_UTF8_VALUE}, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    @ApiOperation(value = "上传或替换图表文件")
    public ResponseEntity uploadOrReplaceChartFile(@ApiParam(value = "上传类型 1 js文件 2 项目文件(zip压缩文件)", required = true) @RequestParam(name = "type", required = true) Integer type,
                                                   @RequestPart(value = "file") MultipartFile file) throws Exception {
        if (file.isEmpty()) {
            return new ResponseEntity<>(new CommResponse("上传文件不能为空", Boolean.FALSE), HttpStatus.BAD_REQUEST);
        }
        String path = "";
        if (type == 1) {
            path = chartFileUrl;
            PutObjectOptions putObjectOptions = new PutObjectOptions(file.getInputStream().available(), -1);
            String regJs = ".+(.js)$";
            String regHtml = ".+(.html)$";
            String regCss = ".+(.css)$";
            Matcher matcherJs = Pattern.compile(regJs).matcher(file.getOriginalFilename());
            Matcher matcherHtml = Pattern.compile(regHtml).matcher(file.getOriginalFilename());
            Matcher matcherCss = Pattern.compile(regCss).matcher(file.getOriginalFilename());
            if (matcherJs.find()) {
                putObjectOptions.setContentType("application/x-javascript");
            } else if (matcherHtml.find()) {
                putObjectOptions.setContentType("text/html");
            } else if (matcherCss.find()) {
                putObjectOptions.setContentType("text/css");
            }
            String objectName = "";
            if (StringUtils.isEmpty(path)) {
                objectName = file.getOriginalFilename();
            } else {
                objectName = path + "/" + file.getOriginalFilename();
            }
            minioClientHelper.getMinioClient().putObject(chartFileMinioBucke, objectName, file.getInputStream(), putObjectOptions);
            return new ResponseEntity<>(new CommResponse("上传成功", Boolean.TRUE, minioUrl + "/" + chartFileMinioBucke + "/" + objectName + "?X-Amz-Algorithm=AWS4-HMAC-SHA256"), HttpStatus.OK);
        } else if (type == 2) {
            //获得原始文件名
            String oriName = file.getOriginalFilename();
            path = projectUrl;
            String projectName = "";
            if (oriName.matches(".*.zip")) {
                ZipInputStream zipInputStream = null;
                FileInputStream fileInputStream = null;
                try {
                    zipInputStream = new ZipInputStream(file.getInputStream());
                    projectName = zipInputStream.getNextEntry().getName();
                    log.info("projectName" + projectName);
                    File fileCacheDir = new File(cacheFileDir);
                    if (!fileCacheDir.exists()) {
                        fileCacheDir.mkdir();
                    }
                    ZipUtil.unzip(zipInputStream, fileCacheDir);
                    log.info("cacheFileDir" + cacheFileDir);
                    uploadDir(chartFileMinioBucke, cacheFileDir + "/" + projectName, path + "/" + projectName);
                } catch (Exception e) {
                    e.printStackTrace();
                    log.error(e.getMessage());
                } finally {
                    if (fileInputStream != null) {
                        fileInputStream.close();
                    }
                    if (zipInputStream != null) {
                        zipInputStream.close();
                    }
                }
                return new ResponseEntity<>(new CommResponse("上传成功", Boolean.TRUE, minioUrl + "/" + chartFileMinioBucke + "/" + path + "/" + projectName + "index.html?X-Amz-Algorithm=AWS4-HMAC-SHA256"), HttpStatus.OK);
            }
        }
        return new ResponseEntity<>(new CommResponse("上传类型错误,未知类型", Boolean.FALSE), HttpStatus.BAD_REQUEST);
    }

    /**
     * 上传文件夹
     *
     * @param minioBucke
     * @param objPath
     * @param uploadPath
     */
    private void uploadDir(String minioBucke, String objPath, String uploadPath) {
        File uploadFile = new File(objPath);
        if (uploadFile.isDirectory()) {
            File fa[] = uploadFile.listFiles();
            for (int i = 0; i < fa.length; i++) {
                File fs = fa[i];
                if (fs.isDirectory()) {
                    uploadDir(minioBucke, objPath + "/" + fs.getName(), uploadPath + "/" + fs.getName());
                } else {
                    uploadDirToMinio(minioBucke, new File(uploadFile + "/" + fs.getName()), uploadPath + "/" + fs.getName());
                }
            }
        } else {
            uploadDirToMinio(minioBucke, uploadFile, uploadPath);
        }

    }

    /**
     * 上传文件到minio
     *
     * @param minioBucke
     * @param uploadFile
     * @param uploadPath
     */
    private void uploadDirToMinio(String minioBucke, File uploadFile, String uploadPath) {
        FileInputStream fileInputStream = null;
        try {
            fileInputStream = new FileInputStream(uploadFile);
            PutObjectOptions putObjectOptions = new PutObjectOptions(fileInputStream.available(), -1);
            String regJs = ".+(.js)$";
            String regHtml = ".+(.html)$";
            String regCss = ".+(.css)$";
            Matcher matcherJs = Pattern.compile(regJs).matcher(uploadFile.getName());
            Matcher matcherHtml = Pattern.compile(regHtml).matcher(uploadFile.getName());
            Matcher matcherCss = Pattern.compile(regCss).matcher(uploadFile.getName());
            if (matcherJs.find()) {
                putObjectOptions.setContentType("application/x-javascript");
            } else if (matcherHtml.find()) {
                putObjectOptions.setContentType("text/html");
            } else if (matcherCss.find()) {
                putObjectOptions.setContentType("text/css");
            }
            minioClientHelper.getMinioClient().putObject(minioBucke, uploadPath, fileInputStream, putObjectOptions);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (fileInputStream != null) {
                try {
                    fileInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 选择用缓冲区来实现这个转换即使用java 创建的临时文件 使用 MultipartFile.transferto()方法 。
     *
     * @param file
     * @return
     */
    private File transferToFile(MultipartFile file) {
        File saveFile = null;
        try {

            //生成临时目录
            File savedFile = new File(cacheFileDir + "/" + file.getOriginalFilename());
            // 判断路径是否存在，不存在则新创建一个
            if (!savedFile.getParentFile().exists()) {
                savedFile.getParentFile().mkdirs();
            }
            // 使用下面的jar包
            FileUtils.copyInputStreamToFile(file.getInputStream(), savedFile);
            InputStream inputStream = new FileInputStream(savedFile);

            //关闭流，才能进行删除临时文件
            inputStream.close();

            //程序结束时，删除临时文件
            deleteFile(savedFile.getAbsolutePath());
        } catch (IOException e) {
            e.printStackTrace();
        }
        return saveFile;
    }


    public static boolean deleteFile(String fileName) {
        File file = new File(fileName);
        if (file.isFile() && file.exists()) {
            Boolean succeedDelete = file.delete();
            if (succeedDelete) {
                System.out.println("删除单个文件" + fileName + "成功！");
                return true;
            } else {
                System.out.println("删除单个文件" + fileName + "失败！");
                return true;
            }
        } else {
            System.out.println("删除单个文件" + fileName + "失败！");
            return false;
        }
    }

    @PostMapping(value = "/getFileContent")
    @ApiOperation(value = "下载文件")
    public void getFileContent(@RequestParam(name = "objPath", required = true) String objPath,
                               @RequestParam(name = "bucketName", required = false) String bucketName,
                               HttpServletResponse response, HttpServletRequest request) throws Exception {
        InputStream inputStream = null;
        try {
            inputStream = minioClientHelper.getMinioClient().getObject(StringUtils.isEmpty(bucketName) ? defaultBucket : bucketName, objPath);
            response.setHeader("Content-Disposition", "attachment;filename=" + objPath);
            response.setContentType("application/force-download");
            response.setCharacterEncoding("UTF-8");
            IoUtil.copy(inputStream, response.getOutputStream());
        } catch (ErrorResponseException e) {
            throw new CustomException("文件已删除");
        } catch (InvalidBucketNameException e) {
            throw new CustomException("文件桶不存在");
        } catch (IllegalArgumentException e) {
            throw new CustomException("文件不存在");
        } catch (Exception e) {
            throw new CustomException("删除文件失败,请稍后再试");
        } finally {
            if (inputStream != null) {
                inputStream.close();
            }
        }
    }

    public static String toConvertStringAndReplace(InputStream is, String replaceString, String replaceKey) {
        StringBuffer res = new StringBuffer();
        InputStreamReader isr = new InputStreamReader(is);
        BufferedReader read = new BufferedReader(isr);
        try {
            String line;
            line = read.readLine();
            while (line != null) {
                if (line.contains(replaceKey)) {
                    line = replaceKey + "=\"" + replaceString + "\"";
                }
                res.append(line + "\n");
                line = read.readLine();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != isr) {
                    isr.close();
                    isr.close();
                }
                if (null != read) {
                    read.close();
                    read = null;
                }
                if (null != is) {
                    is.close();
                    is = null;
                }
            } catch (IOException e) {
            }
        }
        return res.toString();
    }
    /*	 * @PostMapping(value = "presignedGetUrl")
     *
     * @ApiOperation(value = "获取文件下载地址")
     *
     * @ApiImplicitParams({
     *
     * @ApiImplicitParam(name = "objPath", required = true, value = "文件相对路径"),
     *
     * @ApiImplicitParam(name = "bucketName", value = "桶名"),
     *
     * @ApiImplicitParam(name = "expires", value = "过期时间，以秒为单位") })
     *
     * @ApiResponses({
     *
     * @ApiResponse(code = 200, message = "获取成功", response = MinioObject.class) })
     * public MinioObject postPresignedGetUrl(@RequestParam(name = "objPath") String
     * objPath,
     *
     * @RequestParam(name = "bucketName") String bucketName,
     *
     * @RequestParam(name = "expires", required = false) Integer expires) throws
     * Exception { return new
     * MinioObject(minioClientHelper.presignedGetObject(objPath, bucketName,
     * expires), objPath); }
     */


    @RequestMapping(value = "/removeObject", method = RequestMethod.DELETE)
    @ApiOperation(value = "文件删除")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "bucketName", value = "桶名"),
            @ApiImplicitParam(name = "objectName", value = "文件名")
    })
    public ResponseEntity<?> removeObject(@RequestParam(name = "bucketName") String bucketName,
                                          @RequestParam(name = "objectName") String objectName) {
        try {
            minioClientHelper.getMinioClient().removeObject(bucketName, objectName);
            return new ResponseEntity<>(SpringStyleResponseUtil.getError("200", "删除文件成功！", "成功！"), HttpStatus.OK);
        } catch (ErrorResponseException e) {
            throw new CustomException("文件已删除");
        } catch (InvalidBucketNameException e) {
            throw new CustomException("文件桶不存在");
        } catch (IllegalArgumentException e) {
            throw new CustomException("文件不存在");
        } catch (Exception e) {
            return new ResponseEntity<>(SpringStyleResponseUtil.getError("400", "删除文件失败！", "失败！"), HttpStatus.BAD_REQUEST);
        }
    }

    @PostMapping(value = "createBucket")
    @ApiOperation(value = "创建桶")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "bucketName", value = "桶名")
    })
    @ApiResponses({
            @ApiResponse(code = 201, message = "创建成功"),
            @ApiResponse(code = 400, message = "创建失败")
    })
    public ResponseEntity<?> createBucket(@RequestParam(name = "bucketName") String bucketName) throws Exception {
        if (!minioClientHelper.getMinioClient().bucketExists(bucketName)) {
            return new ResponseEntity<>(SpringStyleResponseUtil.getError("400", "错误的请求参数！", "桶名重复！"), HttpStatus.BAD_REQUEST);
        } else {
            // 创建名为'my-bucketname'的存储桶。
            minioClientHelper.getMinioClient().makeBucket(bucketName);
            return new ResponseEntity<>(HttpStatus.CREATED);
        }
    }

    @PutMapping(value = "setBucketPolicy")
    @ApiOperation(value = "设置桶策略")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "bucketName", value = "桶名"),
            @ApiImplicitParam(name = "policy", value = "策略,可选值有[PolicyType.NONE, PolicyType.READ_ONLY, PolicyType.READ_WRITE, PolicyType.WRITE_ONLY]")
    })
    @ApiResponses({
            @ApiResponse(code = 200, message = "修改成功")
    })
    public ResponseEntity<?> setBucketPolicy(@RequestParam(name = "bucketName") String bucketName,
                                             @RequestParam(name = "policy") String policy) throws Exception {
        minioClientHelper.getMinioClient().setBucketPolicy(bucketName, policy);
        return new ResponseEntity<>(HttpStatus.OK);
    }

    @RequestMapping(value = "setAutograph")
    @ApiOperation(value = "获取文件签名")
    @ApiResponses({
            @ApiResponse(code = 200, message = "签名成功")
    })
    @ResponseBody
    public String[] autograph(@RequestBody String[] str) throws Exception {
        for (int i = 0; i < str.length; i++) {
            if (org.apache.commons.lang3.StringUtils.isNotBlank(str[i])) {
                str[i] = minioClientHelper.presignedGetObject(str[i], minioConfig.getDefaultBucket(), 7200);
            }
        }
        return str;
    }
}
